import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Scene } from "@babylonjs/core/scene"
import { Helper } from "../tool/helper";
import { BabylonContainer } from "../base/babylon-container";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { MIN_FLOAT } from "../tool/calculation";


/**
 * 被收起对象的信息
 * @param id 对象id
 * @param checkCollisions 开启碰撞
 * @param isPickable 可拾取
 * @param isVisible 可见
 * @param scaling 缩放
 */
interface IModelAccessorPackInfo {
    id: string | number,
    checkCollisions: boolean,
    isPickable: boolean,
    isVisible: boolean,
    scaling: Vector3,
}


/**
 * 模型寄存
 */
class ModelAccessor {

    private static _scene?: Scene;
    private static _targets: TransformNode[] = [];
    private static _scenes: Scene[] = [];
    private static _packInfos: IModelAccessorPackInfo[] = [];

    /**
     * 将目标对象收起（不在场景中使用）
     * @param targets 目标对象集合
     * @returns 成功
     */
    public static pack(...targets: TransformNode[]): boolean {
        let sus = false;
        for (const target of targets) {
            let est = false;
            for (const info of this._packInfos) {
                if (info.id === target.id) {
                    est = true;
                    break;
                }
            }
            if (est) continue;
            this._packInfos.push({
                id: target.id,
                scaling: new Vector3().copyFrom(target.scaling),
                checkCollisions: (target as AbstractMesh).checkCollisions,
                isVisible: (target as AbstractMesh).isVisible,
                isPickable: (target as AbstractMesh).isPickable,
            });
            target.scaling.set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
            (target as AbstractMesh).isPickable = false;
            (target as AbstractMesh).isVisible = false;
            (target as AbstractMesh).checkCollisions = false;
            sus = true;
        }
        return sus;
    }

    /**
     * 将目标对象取出（在场景中使用）
     * @param targets 目标对象集合
     * @returns 成功
     */
    public static unpack(...targets: TransformNode[]): boolean {
        let sus = false;
        for (const target of targets) {
            for (let i = 0; i < this._packInfos.length; i++) {
                const info = this._packInfos[i];
                if (info.id !== target.id) continue;
                target.scaling.copyFrom(info.scaling);
                (target as AbstractMesh).isPickable = info.isPickable;
                (target as AbstractMesh).checkCollisions = info.checkCollisions;
                (target as AbstractMesh).isVisible = info.isVisible;
                this._packInfos.splice(i, 1);
                sus = true;
                i--;
            }
        }
        return sus;
    }

    /**
     * 获取寄存模型的场景
     */
    public static get scene(): Scene {
        if (!this._scene) this.initialize();
        return this._scene!;
    }

    /**
     * 存放模型
     * @param targets 要存放的对象
     * @returns 是否存放成功
     */
    public static deposit(...targets: TransformNode[]): boolean {
        if (!this._scene) this.initialize();
        for (const tar of targets) {
            const sus = this.depositInternal(tar);
            if (!sus) return false;
        }
        return true;
    }

    /**
     * 取回寄存的对象
     * @param targets 要取回的对象 
     * @returns 是否成功取回
     */
    public static retrieve(...targets: TransformNode[]): boolean {
        if (!this._scene) this.initialize();
        for (const tar of targets) {
            const sus = this.retriveInternal(tar);
            if (!sus) return false;
        }
        return true;
    }

    /**
     * 初始化
     */
    private static initialize(): void {
        if (!this._scene) this._scene = BabylonContainer.createScene()!;
        this._targets.length = 0;
        this._scenes.length = 0;
    }

    /**
     * 模型取回内部函数
     * @param target  要取回的对象
     * @returns 是否成功取回
     */
    private static retriveInternal(target: TransformNode): boolean {
        if (!this._scene) return false;
        const idx = this._targets.findIndex(tar => tar.id === target.id);
        if (idx < 0) return false;
        const osn = this._scenes[idx];
        const meshes = Helper.getChildMeshes(target);
        const zs = ModelAccessor;
        meshes.forEach(m => {
            zs._scene!.removeMesh(m);
            if (osn) osn.addMesh(m);
        });
        this._targets.splice(idx, 1);
        this._scenes.splice(idx, 1);
        return true;
    }

    /**
     * 模型寄存内部函数
     * @param target 要寄存的对象
     * @returns 是否寄存成功
     */
    private static depositInternal(target: TransformNode): boolean {
        if (!this._scene) return false;
        const meshes = Helper.getChildMeshes(target);
        const osn = target.getScene();
        const zs = ModelAccessor;
        meshes.forEach(m => {
            if (osn) osn.removeMesh(m);
            zs._scene!.addMesh(m);
        });
        if (this._targets.includes(target)) return false;
        this._scenes.push(osn);
        this._targets.push(target);
        return true;
    }

}


export { ModelAccessor };